/* * grEMF * * Copyright (C) 2006-2012 Institute for Software Technology * University of Koblenz-Landau, Germany * ist@uni-koblenz.de * * For bug reports, documentation and further information, visit * * https://github.com/jgralab/gremf * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see <http://www.gnu.org/licenses>. * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with Eclipse (or a modified version of that program or an Eclipse * plugin), containing parts covered by the terms of the Eclipse Public * License (EPL), the licensors of this Program grant you additional * permission to convey the resulting work. Corresponding Source for a * non-source form of such a combination shall include the source code for * the parts of JGraLab used as well as that of the covered work. */ package de.uni_koblenz.gremf.resource; import java.io.File; import java.io.IOException; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.eclipse.emf.common.notify.impl.NotificationChainImpl; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EAnnotation; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EDataType; import org.eclipse.emf.ecore.EEnum; import org.eclipse.emf.ecore.EEnumLiteral; import org.eclipse.emf.ecore.EModelElement; import org.eclipse.emf.ecore.ENamedElement; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.impl.EStructuralFeatureImpl; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.BasicFeatureMap; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.ecore.util.FeatureMap; import org.eclipse.emf.ecore.util.FeatureMapUtil; import de.uni_koblenz.gremf.exception.SchemaLoadException; import de.uni_koblenz.gremf.schema.impl.GrEMFAttributeImpl; import de.uni_koblenz.gremf.schema.impl.GrEMFEdgeClassImpl; import de.uni_koblenz.gremf.schema.impl.GrEMFEnumDomainImpl; import de.uni_koblenz.gremf.schema.impl.GrEMFGraphClassImpl; import de.uni_koblenz.gremf.schema.impl.GrEMFIncidenceClassImpl; import de.uni_koblenz.gremf.schema.impl.GrEMFIncidenceClassWithRefsImpl; import de.uni_koblenz.gremf.schema.impl.GrEMFPackageImpl; import de.uni_koblenz.gremf.schema.impl.GrEMFRefEdgeClassImpl; import de.uni_koblenz.gremf.schema.impl.GrEMFSchemaImpl; import de.uni_koblenz.gremf.schema.impl.GrEMFVertexClassImpl; import de.uni_koblenz.jgralab.schema.AggregationKind; import de.uni_koblenz.jgralab.schema.AttributedElementClass; import de.uni_koblenz.jgralab.schema.Domain; import de.uni_koblenz.jgralab.schema.EdgeClass; import de.uni_koblenz.jgralab.schema.Package; import de.uni_koblenz.jgralab.schema.VertexClass; /** * Part of the resource handler for loading EMF schemas into grEMF<br> * The EMF types are transformed into the corresponding grEMF types. * * @see {@link de.uni_koblenz.gremf.resource.GrEMFSchemaResourceHandler * GrEMFResourceHandler} (handler for grEMF resources) * */ public class GrEMFSchemaTransformer { private Resource resource; private GrEMFSchemaImpl schema; private GrEMFGraphClassImpl graph; // marker sets private Set<EClass> vertexClasses; private Set<EClass> edgeClasses; private Set<EEnum> enumDomains; private Set<EAttribute> attributes; private Set<EReference> incidenceClasses; private Set<EPackage> packages; /** * ascending order of the vertex class by the number of all supertypes */ private EClass[] vClassOrder; /** * ascending order of the edge class by the number of all supertypes */ private EClass[] eClassOrder; GrEMFSchemaTransformer(Resource resource) { this.resource = resource; // init sets this.vertexClasses = new HashSet<EClass>(); this.edgeClasses = new HashSet<EClass>(); this.enumDomains = new HashSet<EEnum>(); this.attributes = new HashSet<EAttribute>(); this.incidenceClasses = new HashSet<EReference>(); this.packages = new HashSet<EPackage>(); // gather all associated content this.resolveAllContent(); // init graph this.schema = new GrEMFSchemaImpl("Schema", createSchemaPackagePrefix(resource)); this.graph = this.schema.createGraphClass("Graph" + Character.toUpperCase(this.schema.getPackagePrefix() .charAt(0)) + this.schema.getPackagePrefix().substring(1)); } /** * Resolves all contents by visiting all object of involved resources. This * includes proxy objects as well. If the resource of a proxy object is not * present, it is tried to load the resource file via its URI. */ private void resolveAllContent() { this.resolveResourceContent(this.resource); for (EObject proxyObj : EcoreUtil.ProxyCrossReferencer.find( this.resource).keySet()) { String otherResourceURI = ((InternalEObject) proxyObj).eProxyURI() .path(); // ignore the "default" or the same resource if (otherResourceURI.equals(this.resource.getURI().path()) || otherResourceURI.equals(EcorePackage.eINSTANCE .eResource().getURI().path())) { continue; } // lookup in resource set Resource otherResource = this.resource.getResourceSet() .getResource(URI.createURI(otherResourceURI), false); // or try to get the resource manually if (otherResource == null) { File proxySchema = new File(otherResourceURI); if (proxySchema.exists()) { otherResource = this.resource.getResourceSet() .createResource( URI.createURI(proxySchema.getPath())); } else { throw new SchemaLoadException(otherResource); } } // load normally: without specific ResourceHandler Map<Object, Object> options = ((GrEMFSchemaResourceImpl) otherResource) .getDefaultLoadOptions(); ((GrEMFSchemaResourceImpl) otherResource).getDefaultLoadOptions() .clear(); // try to load the resource try { otherResource.load(Collections.EMPTY_MAP); } catch (IOException e) { throw new SchemaLoadException(otherResource); } ((GrEMFSchemaResourceImpl) otherResource).getDefaultLoadOptions() .putAll(options); this.resolveResourceContent(otherResource); } // build class orders this.vClassOrder = buildOrder(this.vertexClasses); this.eClassOrder = buildOrder(this.edgeClasses); } /** * Resolves the grEMF types of the contained EMF types of the given * resource. Therefore, all contained elements are visited and added to the * corresponding sets. Additionally, the transformation order of the vertex * and edge classes is built. <br> * Note that not all resolved edge classes necessarily are transformed to * such type. Some may become vertex classes. * * @param resource * input <code>Resource</code> whose content is resolved */ private void resolveResourceContent(Resource resource) { TreeIterator<EObject> i = resource.getAllContents(); while (i.hasNext()) { EObject eObj = i.next(); if (eObj instanceof EPackage) { // implicitly created via EClass and EEnum this.packages.add((EPackage) eObj); } else if (eObj instanceof EClass) { if (isVertexClassCertainly((EClass) eObj)) { this.vertexClasses.add((EClass) eObj); } else { this.edgeClasses.add((EClass) eObj); } } else if (eObj instanceof EEnum) { this.enumDomains.add((EEnum) eObj); } else if (eObj instanceof EAttribute) { EAttribute eAttr = (EAttribute) eObj; if (FeatureMapUtil.isFeatureMap(eAttr)) { this.resolveFeatureMap(new BasicFeatureMap( (InternalEObject) eAttr.getEContainingClass(), eAttr.getFeatureID())); } else { this.attributes.add(eAttr); } } else if (eObj instanceof EReference) { if (!this.incidenceClasses.contains(((EReference) eObj) .getEOpposite())) { this.incidenceClasses.add((EReference) eObj); } } else if (eObj instanceof EAnnotation) { // nothing to do; done via the annotated element } } } /** * Resolves EMF's feature map as single features * * @param map * feature map */ private void resolveFeatureMap(FeatureMap map) { for (FeatureMap.Entry e : map) { EStructuralFeature feature = e.getEStructuralFeature(); if (feature instanceof EReference) { if (!this.incidenceClasses.contains(((EReference) feature) .getEOpposite())) { this.incidenceClasses.add((EReference) feature); } } else { this.attributes.add((EAttribute) feature); } } } /** * Transforms the resolved EMF types to their corresponding grEMF types. * * @see {@link #resolveAllContent}, {@link #resolveResourceContent} */ void transform() { for (EClass eCls : this.vClassOrder) { this.extractVertexClass(eCls); } for (EClass eCls : this.eClassOrder) { this.extractEdgeClass(eCls); } for (EEnum eEnum : this.enumDomains) { this.extractEnumDomain(eEnum); } for (EReference eRef : this.incidenceClasses) { this.extractIncidenceClass(eRef); } for (EAttribute eAttr : this.attributes) { this.extractAttributes(eAttr); } for (EPackage ePkg : this.packages) { this.extractPackages(ePkg); } } /** * Finishes the transformation by: <br> * <ul> * <li>finishing the schema</li> * <li>clearing the resource</li> * <li>adding the transformed content to the resource</li> * </ul> * and * <ul> * <li>clearing all collections</li> * </ul> */ void finish() { // get root package GrEMFPackageImpl rootPkg = (GrEMFPackageImpl) this.schema .getDefaultPackage(); // handle root (=default) package rootPkg.setNsPrefix(this.schema.getPackagePrefix()); rootPkg.setNsURI("http://" + rootPkg.getNsPrefix() + ".com"); // register package EPackage.Registry.INSTANCE.put(rootPkg.getNsURI(), rootPkg); // modify resource this.resource.getContents().clear(); this.resource.getContents().add(rootPkg); // tell package that it is in a resource rootPkg.eSetResource((Resource.Internal) this.resource, new NotificationChainImpl()); // register package in resource set this.resource.getResourceSet().getPackageRegistry() .put(rootPkg.getNsURI(), rootPkg); // finish schema this.schema.finish(); } private void extractPackages(EPackage ePkg) { GrEMFPackageImpl p = (GrEMFPackageImpl) this.schema .getPackage(getPackageName(ePkg)); // copy all other properties copyProperties(ePkg, p); // add comments copyAnnotations(ePkg, p); // register package EPackage.Registry.INSTANCE.put(p.getNsURI(), p); if (this.resource.getResourceSet() != null) { this.resource.getResourceSet().getPackageRegistry() .put(p.getNsURI(), p); } } /** * Extracts the grEMF <code>GrEMFVertexClassImpl</code> using the given EMF * <code>EClass</code>. * * Note that supertypes are extracted first. * * <dt><b>Preconditions:</b> * <dd> * <code>this.isVertexClass(eCls) == true</code></dd></dt> * * @param eCls * input <code>EClass</code> */ private void extractVertexClass(EClass eCls) { // get package prefix String pkgPrefix = getPackagePrefix(eCls); // create vertex class GrEMFVertexClassImpl vertexCls = this.graph .createVertexClass(createQualifiedName(pkgPrefix, eCls.getName())); // add supertypes for (EClass supertype : eCls.getESuperTypes()) { // supertype is already extracted vertexCls.addSuperClass(this.getVertexClass(supertype)); } // copy all other properties copyProperties(eCls, vertexCls); // add comments copyAnnotations(eCls, vertexCls); } /** * Extracts the grEMF <code>GrEMFEnumDomainImpl</code> using the given EMF * <code>EEnum</code>. * * @param eEnum * input <code>EEnum</code> */ private void extractEnumDomain(EEnum eEnum) { // get package String pkgPrefix = getPackagePrefix(eEnum); // create enum domain GrEMFEnumDomainImpl enumDom = this.schema .createEnumDomain(createQualifiedName(pkgPrefix, eEnum.getName())); // add literals for (EEnumLiteral eEnumLit : eEnum.getELiterals()) { enumDom.addConst(eEnumLit.getLiteral()); } // copy attributes copyProperties(eEnum, enumDom); // add comments copyAnnotations(eEnum, enumDom); } /** * Extracts the grEMF <code>GrEMFIncidenceClassImpl</code> using the given * EMF <code>EReference</code>. * * @param eRef * input <code>EReference</code> */ private void extractIncidenceClass(EReference eRef) { // scenario: v1-->v2 // get vertex classes from this reference GrEMFVertexClassImpl vertexCls1 = this.getVertexClass(eRef .getEContainingClass()); GrEMFVertexClassImpl vertexCls2 = this.getVertexClass(eRef .getEReferenceType()); // get rolenames // v1-->v2, v1<--v2 String v1RoleName = getRoleName(eRef.getEOpposite(), eRef.getEContainingClass(), eRef); // v1-->v2 String v2RoleName = getRoleName(eRef, eRef.getEReferenceType(), eRef); // get aggregation kinds // v1-->v2, v1<-<>v2: eRef.isContainer() => v1:COMPOSITE AggregationKind v1Aggr = eRef.isContainer() ? AggregationKind.COMPOSITE : AggregationKind.NONE; // v1<>->v2: eRef.isContainment() => v2:COMPOSITE AggregationKind v2Aggr = eRef.isContainment() ? AggregationKind.COMPOSITE : AggregationKind.NONE; // get multiplicities int v1Min = getMinMultiplicity(eRef.getEOpposite()); int v2Min = getMinMultiplicity(eRef); int v1Max = getMaxMultiplicity(eRef.getEOpposite(), v2Aggr); int v2Max = getMaxMultiplicity(eRef, v1Aggr); // required property if (eRef.isRequired() && (v1Min < 1)) { v1Min = 1; } if ((eRef.getEOpposite() != null) && eRef.getEOpposite().isRequired() && (v2Min < 1)) { v2Min = 1; } GrEMFRefEdgeClassImpl edgeCls; if ((eRef.getEOpposite() == null) || ((v1Aggr != AggregationKind.COMPOSITE) && ((v2Aggr == AggregationKind.COMPOSITE) || (vertexCls1 .getName().compareTo(vertexCls2.getName()) <= 0)))) { edgeCls = this.createRefEdgeClass(vertexCls1, v1RoleName, v1Aggr, v1Min, v1Max, vertexCls2, v2RoleName, v2Aggr, v2Min, v2Max); processIncidenceClass((GrEMFIncidenceClassImpl) edgeCls.getFrom(), eRef.getEOpposite()); } else { edgeCls = this.createRefEdgeClass(vertexCls2, v2RoleName, v2Aggr, v2Min, v2Max, vertexCls1, v1RoleName, v1Aggr, v1Min, v1Max); processIncidenceClass((GrEMFIncidenceClassImpl) edgeCls.getTo(), eRef.getEOpposite()); } // copy other properties copyProperties(eRef, (GrEMFIncidenceClassImpl) edgeCls.getTo()); copyProperties(eRef.getEOpposite(), (GrEMFIncidenceClassImpl) edgeCls.getFrom()); // add comments copyAnnotations(eRef, (GrEMFIncidenceClassImpl) edgeCls.getTo()); copyAnnotations(eRef.getEOpposite(), (GrEMFIncidenceClassImpl) edgeCls.getFrom()); } /** * Tries to extract the grEMF <code>GrEMFEdgeClassImpl</code> using the * given EMF <code>EClass</code>. If it fails, a * <code>GrEMFVertexClassImpl</code> is extracted. Note that the supertypes * are extracted first. * * <dt><b>Preconditions:</b> * <dd> * <code>this.isVertexClass(eCls) == false</code></dd></dt> * * @param eCls * input <code>EClass</code> */ private void extractEdgeClass(EClass eCls) { // get the relevant references EReference[] edgeClsOuts = new EReference[2]; EReference[] edgeClsIns = new EReference[2]; int i = 0; for (EReference eRef : eCls.getEReferences()) { if ((eRef.getLowerBound() == 1) && (eRef.getUpperBound() == 1)) { edgeClsOuts[i] = eRef; edgeClsIns[i++] = eRef.getEOpposite(); } } // get vertex classes from the end references GrEMFVertexClassImpl vertexCls1 = this.getVertexClass(edgeClsOuts[0] .getEReferenceType()); GrEMFVertexClassImpl vertexCls2 = this.getVertexClass(edgeClsOuts[1] .getEReferenceType()); // get rolenames String v1RoleName = "grEMF_" + getRoleName(edgeClsIns[1], vertexCls1, edgeClsIns[0]); String v2RoleName = "grEMF_" + getRoleName(edgeClsIns[0], vertexCls2, edgeClsIns[1]); // get aggregation kinds AggregationKind v1Aggr = getAggregationKind(edgeClsIns[1], edgeClsOuts[0]); AggregationKind v2Aggr = getAggregationKind(edgeClsIns[0], edgeClsOuts[1]); // at least one edge must be of type NONE if ((v1Aggr != AggregationKind.NONE) && (v2Aggr != AggregationKind.NONE)) { // this is not an edge class this.extractVertexClass(eCls); return; } // get multiplicity int v1Min = getMinMultiplicity(edgeClsIns[1]); int v2Min = getMinMultiplicity(edgeClsIns[0]); int v1Max = getMaxMultiplicity(edgeClsIns[1], v2Aggr); int v2Max = getMaxMultiplicity(edgeClsIns[0], v1Aggr); // qualified name of this edge class String qualifiedName = getPackagePrefix(eCls) + "." + eCls.getName(); // supertypes and subtypes must be compatible edge classes // check supertypes for (EClass supertype : eCls.getESuperTypes()) {// get package Package pkg = this.schema.getPackage(getPackagePrefix(supertype)); if (pkg == null) { // error } else if (pkg.getEdgeClass(supertype.getName()) == null) { // this is not an edge class this.extractVertexClass(eCls); return; } else { // supertype is an edge class EdgeClass sEdgeCls = pkg.getEdgeClass(supertype.getName()); if (!this.isCompatibleEdgeClass(vertexCls1, vertexCls2, sEdgeCls)) { // this is not an edge class this.extractVertexClass(eCls); return; } } } GrEMFEdgeClassImpl edgeCls; if ((v1Aggr != AggregationKind.COMPOSITE) && ((v2Aggr == AggregationKind.COMPOSITE) || (vertexCls1 .getName().compareTo(vertexCls2.getName()) <= 0))) { // v1 == from, v2 == to edgeCls = this.createEdgeClass(qualifiedName, vertexCls1, v1RoleName, v1Aggr, v1Min, v1Max, vertexCls2, v2RoleName, v2Aggr, v2Min, v2Max, edgeClsOuts, edgeClsIns, 0); processIncidenceClass((GrEMFIncidenceClassImpl) edgeCls.getFrom(), edgeClsIns[1]); processIncidenceClass((GrEMFIncidenceClassImpl) edgeCls.getTo(), edgeClsIns[0]); } else { // v2 == from, v1 == to edgeCls = this.createEdgeClass(qualifiedName, vertexCls2, v2RoleName, v2Aggr, v2Min, v2Max, vertexCls1, v1RoleName, v1Aggr, v1Min, v1Max, edgeClsOuts, edgeClsIns, 1); processIncidenceClass((GrEMFIncidenceClassImpl) edgeCls.getFrom(), edgeClsIns[0]); processIncidenceClass((GrEMFIncidenceClassImpl) edgeCls.getTo(), edgeClsIns[1]); } // add supertypes for (EClass supertype : eCls.getESuperTypes()) { // supertype is already extracted edgeCls.addSuperClass(this.getEdgeClass(supertype)); } // copy all other properties copyProperties(eCls, edgeCls); // add comments copyAnnotations(eCls, edgeCls); // remove references from the sets if its an edge class this.incidenceClasses.remove(edgeClsOuts[0]); this.incidenceClasses.remove(edgeClsOuts[1]); this.incidenceClasses.remove(edgeClsIns[0]); this.incidenceClasses.remove(edgeClsIns[1]); } /** * Determines whether an edge class with its both ends is compatible to a * super edge class. * * @param vCls1 * one incident vertex class; one end * @param vCls2 * other incident vertex class; other end * @param superEdgeCls * super edge class * @return true, if vCls1 as "from" vertex class and vCls2 as "to" vertex * class are compatible to the super edge class<br> * true, if vCls1 as "to" vertex class and vCls2 as "from" vertex * class are compatible to the super edge class<br> * false, otherwise */ private boolean isCompatibleEdgeClass(GrEMFVertexClassImpl vCls1, GrEMFVertexClassImpl vCls2, EdgeClass superEdgeCls) { VertexClass sFrom = superEdgeCls.getFrom().getVertexClass(); VertexClass sTo = superEdgeCls.getTo().getVertexClass(); return (this.isCompatibleEnd(vCls1, sFrom) && this.isCompatibleEnd( vCls2, sTo)) || (this.isCompatibleEnd(vCls1, sTo) && this.isCompatibleEnd( vCls2, sFrom)); } /** * Determines whether an edge class end is compatible to the end of a super * edge class. * * @param end * end of a edge class * @param superEnd * end of a super edge class * @return true, if superEnd is a supertype of end or superEnd is equal to * end<br> * false, otherwise */ private boolean isCompatibleEnd(GrEMFVertexClassImpl end, VertexClass superEnd) { return superEnd.isSuperClassOf(end) || superEnd.equals(end); } /** * Extracts the grEMF <code>GrEMFAttributeImpl</code> using the given EMF * <code>EAttribute</code>. * * @param eAttr * input <code>EAttribute</code> */ private void extractAttributes(EAttribute eAttr) { EcorePackage ecorePkg = EcorePackage.eINSTANCE; // get package Package pkg = this.schema.getPackage(getPackagePrefix(eAttr)); // get base domain EDataType eDataTp = eAttr.getEAttributeType(); Domain dom = this.getDomain(ecorePkg, pkg, eDataTp); // get container AttributedElementClass<?, ?> container = this.getVertexClass(eAttr .getEContainingClass()); if (container == null) { container = this.getEdgeClass(eAttr.getEContainingClass()); } // bounds and ordered property if (eAttr.isOrdered() && !eAttr.isUnique() && ((eAttr.getUpperBound() > 1) || (eAttr.getUpperBound() == EStructuralFeature.UNBOUNDED_MULTIPLICITY))) { dom = this.schema.createListDomain(dom); } else if (!eAttr.isOrdered() && eAttr.isUnique() && ((eAttr.getUpperBound() > 1) || (eAttr.getUpperBound() == EStructuralFeature.UNBOUNDED_MULTIPLICITY))) { dom = this.schema.createSetDomain(dom); } else if ((eAttr.getUpperBound() > 1) || (eAttr.getUpperBound() == EStructuralFeature.UNBOUNDED_MULTIPLICITY)) { dom = this.schema.createListDomain(dom); } GrEMFAttributeImpl attr = (GrEMFAttributeImpl) container .createAttribute(eAttr.getName(), dom, eAttr.getDefaultValueLiteral()); // copy properties and comments copyProperties(eAttr, attr); copyAnnotations(eAttr, attr); } private Domain getDomain(EcorePackage ecorePkg, Package pkg, EDataType eDataTp) { Domain dom = null; // primitives if (eDataTp.equals(ecorePkg.getEInt())) { dom = this.schema.getIntegerDomain(); } else if (eDataTp.equals(ecorePkg.getEDouble())) { dom = this.schema.getDoubleDomain(); } else if (eDataTp.equals(ecorePkg.getEBoolean())) { dom = this.schema.getBooleanDomain(); } else if (eDataTp.equals(ecorePkg.getEByte())) { dom = this.schema.getByteDomain(); } else if (eDataTp.equals(ecorePkg.getEShort())) { dom = this.schema.getShortDomain(); } else if (eDataTp.equals(ecorePkg.getELong())) { dom = this.schema.getLongDomain(); } else if (eDataTp.equals(ecorePkg.getEFloat())) { dom = this.schema.getFloatDomain(); } else if (eDataTp.equals(ecorePkg.getEChar())) { dom = this.schema.getCharDomain(); } // others: string, enum, date else if (eDataTp.equals(ecorePkg.getEString())) { dom = this.schema.getStringDomain(); } else if (eDataTp instanceof EEnum) { dom = pkg.getDomain(eDataTp.getName()); } else if (eDataTp.equals(ecorePkg.getEDate())) { dom = this.schema.getDateDomain(); } // wrapper types else if (eDataTp.equals(ecorePkg.getEIntegerObject())) { dom = this.schema.getIntegerObjectDomain(); } else if (eDataTp.equals(ecorePkg.getEDoubleObject())) { dom = this.schema.getDoubleObjectDomain(); } else if (eDataTp.equals(ecorePkg.getEBooleanObject())) { dom = this.schema.getBooleanDomain(); } else if (eDataTp.equals(ecorePkg.getEByteObject())) { dom = this.schema.getByteObjectDomain(); } else if (eDataTp.equals(ecorePkg.getEShortObject())) { dom = this.schema.getShortObjectDomain(); } else if (eDataTp.equals(ecorePkg.getELongObject())) { dom = this.schema.getLongObjectDomain(); } else if (eDataTp.equals(ecorePkg.getEFloatObject())) { dom = this.schema.getFloatObjectDomain(); } else if (eDataTp.equals(ecorePkg.getECharacterObject())) { dom = this.schema.getCharObjectDomain(); } // big number types else if (eDataTp.equals(ecorePkg.getEBigInteger())) { dom = this.schema.getBigIntegerDomain(); } else if (eDataTp.equals(ecorePkg.getEBigDecimal())) { dom = this.schema.getBigDecimalDomain(); } return dom; } /** * Gets the vertex class corresponding to the given <code>EClass</code>. * * @param eCls * input <code>EClass</code> * @return corresponding <code>VertexClass</code> * @throws IllegalArgumentException * if the passed <code>EClass</code> does not exist */ private GrEMFVertexClassImpl getVertexClass(EClass eCls) { String pkgPrefix = getPackagePrefix(eCls); Package pkg = this.schema.getPackage(pkgPrefix); GrEMFVertexClassImpl end = null; if ((pkg != null) && pkg.containsNamedElement(eCls.getName())) { // must be true end = (GrEMFVertexClassImpl) pkg.getVertexClass(eCls.getName()); } else { throw new IllegalArgumentException( "The passed class does not exist"); } return end; } /** * Gets the edge class corresponding to the given <code>EClass</code>. * * @param eCls * input <code>EClass</code> * @return corresponding <code>EdgeClass</code> * @throws IllegalArgumentException * if the passed <code>EClass</code> does not exist */ private GrEMFEdgeClassImpl getEdgeClass(EClass eCls) { String pkgPrefix = getPackagePrefix(eCls); Package pkg = this.schema.getPackage(pkgPrefix); GrEMFEdgeClassImpl end = null; if ((pkg != null) && pkg.containsNamedElement(eCls.getName())) { // must be true end = (GrEMFEdgeClassImpl) pkg.getEdgeClass(eCls.getName()); } else { throw new IllegalArgumentException( "The passed class does not exist"); } return end; } /** * Creates the edge class from all parameters and processes both incidence * classes. * * @return created edge class * @see {@link #processIncidenceClass(GrEMFIncidenceClassWithRefsImpl, EReference, EReference)} */ private GrEMFEdgeClassImpl createEdgeClass(String qualifiedName, GrEMFVertexClassImpl from, String fromRoleName, AggregationKind fromAggr, int fromMin, int fromMax, GrEMFVertexClassImpl to, String toRoleName, AggregationKind toAggr, int toMin, int toMax, EReference[] edgeClsOuts, EReference[] edgeClsIns, int fromIndex) { // create edge class GrEMFEdgeClassImpl edgeCls = this.graph.createEdgeClass(qualifiedName, from, fromMin, fromMax, fromRoleName, fromAggr, to, toMin, toMax, toRoleName, toAggr); // process incidence classes processIncidenceClass( (GrEMFIncidenceClassWithRefsImpl) edgeCls.getFrom(), edgeClsOuts[fromIndex], edgeClsIns[fromIndex]); processIncidenceClass( (GrEMFIncidenceClassWithRefsImpl) edgeCls.getTo(), edgeClsOuts[(fromIndex + 1) % 2], edgeClsIns[(fromIndex + 1) % 2]); return edgeCls; } /** * Creates an edge class based on an reference. * * @return new edge class */ private GrEMFRefEdgeClassImpl createRefEdgeClass(GrEMFVertexClassImpl from, String fromRoleName, AggregationKind fromAggr, int fromMin, int fromMax, GrEMFVertexClassImpl to, String toRoleName, AggregationKind toAggr, int toMin, int toMax) { // create an name String qualifiedName = createQualifiedName(getPackagePrefix(from), createEdgeClassName(toAggr, toRoleName, from.getName())); return this.graph.createRefEdgeClass(qualifiedName, from, fromMin, fromMax, fromRoleName, fromAggr, to, toMin, toMax, toRoleName, toAggr); } /** * Processes the given incidence class:<br> * Attributes and values from the original references are set to the * incidence's special references. * * @param incCls * processed incidence class * @param edgeClsOut * original reference from the edge class to the incident vertex * class * @param edgeClsIn * original reference to the edge class from the incident vertex * class */ private static void processIncidenceClass( GrEMFIncidenceClassWithRefsImpl incCls, EReference edgeClsOut, EReference edgeClsIn) { // handle fromEdgeCls reference incCls.getFromEdgeClass().setName(edgeClsOut.getName()); incCls.getFromEdgeClass().setContainment(edgeClsOut.isContainment()); if (incCls.getFromEdgeClass() instanceof EStructuralFeatureImpl) { ((EStructuralFeatureImpl) incCls.getFromEdgeClass()) .setFeatureID(edgeClsOut.getFeatureID()); } // handle toEdgeCls reference if (edgeClsIn == null) { incCls.clearToEdgeClass(); } else { incCls.getToEdgeClass().setName(edgeClsIn.getName()); incCls.getToEdgeClass().setLowerBound(edgeClsIn.getLowerBound()); incCls.getToEdgeClass().setUpperBound(edgeClsIn.getUpperBound()); incCls.getToEdgeClass().setContainment(edgeClsIn.isContainment()); if (incCls.getToEdgeClass() instanceof EStructuralFeatureImpl) { ((EStructuralFeatureImpl) incCls.getToEdgeClass()) .setFeatureID(edgeClsIn.getFeatureID()); } } } /** * Processes the given incidence class:<br> * If the incidence class describes a reference that not exists, it became * invisible. * * @param incCls * processed incidence class * @param ref * reference which is described by the incidence class */ private static void processIncidenceClass(GrEMFIncidenceClassImpl incCls, EReference ref) { if (ref == null) { // non existing reference incCls.setInvisibile(true); } copyProperties(ref, incCls); } /** * Determines whether the given EMF <code>EClass</code> is a grEMF * <code>GrEMFVertexClassImpl</code> in any case. If so, return true, * otherwise false. <br> * It is a <code>GrEMFVertexClassImpl</code>, if <br> * - there are no 2 references <br> * - there are no 2 references stating the end of an edge <br> * or <br> * - there is no reference stating the multiplicity of an edge. <br> * <br> * Note that there can be other <code>EClasses</code> being also a * <code>GrEMFVertexClassImpl</code> * * @param eCls * input <code>EClass</code> * @return true if <code>c</code> is a <code>GrEMFVertexClassImpl</code> in * any case */ private static boolean isVertexClassCertainly(EClass eCls) { EList<EReference> eRefs = eCls.getEReferences(); // edge class: 2 references if (eRefs.size() != 2) { return true; } // resolve the end references int i = 0; int multiplicityRefs = 0; for (EReference eRef : eRefs) { if ((eRef.getLowerBound() == 1) && (eRef.getUpperBound() == 1)) { if (i == 2) { // found a third end return true; } i++; if (eRef.getEOpposite() != null) { multiplicityRefs++; } } } // edge class: two references as ends of the edge if ((i != 2) // edge class: one reference as multiplicity of the edge || (multiplicityRefs < 1) // edge class: meaningful relationship between ends || (eRefs.get(0).isContainer() && eRefs.get(1).isContainer()) || (eRefs.get(0).isContainment() && eRefs.get(1) .isContainment())) { return true; } else { return false; } } /** * Builds an ascending order for the given classes based on their number of * supertypes. * * @param classes * input set of <code>EClasses</code> * @return ascending order based on the numbers of supertypes */ private static EClass[] buildOrder(Set<EClass> classes) { // compute greatest offset index int max = 0; for(EClass eCls : classes) { int i = eCls.getEAllSuperTypes().size(); if(i > max) { max = i; } } // offsets int[] offsets = new int[max + 1]; // enter occurrences of number of supertypes for (EClass eCls : classes) { offsets[eCls.getEAllSuperTypes().size()]++; } // apply offset shift int offset = 0; for (int i = 0; i < offsets.length; i++) { int o = offsets[i]; if (o != 0) { offsets[i] = offset; } offset += o; } EClass[] order = new EClass[offset]; for (EClass eCls : classes) { int pos = offsets[eCls.getEAllSuperTypes().size()]; while (order[pos] != null) { pos++; } order[pos] = eCls; } return order; } private static String createQualifiedName(String pkg, String name) { return pkg + '.' + name; } /** * Builds the package prefix of the schema using the names of all root packages * @param resource Resource of the schema model * @return String concatenation of all the names in CamelCase */ private static String createSchemaPackagePrefix(Resource resource) { StringBuilder prefix = new StringBuilder(); for (EObject eObj : resource.getContents()) { if (eObj instanceof ENamedElement) { prefix.append( Character.toUpperCase(((ENamedElement) eObj).getName() .charAt(0))).append( ((ENamedElement) eObj).getName().substring(1)); } } return Character.toLowerCase(prefix.charAt(0)) + prefix.substring(1); } /** * Derives the package name of the given EMF <code>EPackage</code>. * * @param ePkg * input <code>EPackage</code> * @return package name of <code>ePkg</code> */ private static String getPackageName(EPackage ePkg) { return getQualifiedName(ePkg).toLowerCase(); } /** * Derives the package prefix of the given EMF <code>EClass</code>. * * @param eCls * input <code>EClass</code> * @return package prefix of <code>eCls</code> */ private static String getPackagePrefix(EClass eCls) { return getPackageName(eCls.getEPackage()); } /** * Derives the package prefix of the given EMF <code>EEnum</code>. * * @param eNum * input <code>EEnum</code> * @return package prefix of <code>eNum</code> */ private static String getPackagePrefix(EEnum eNum) { return getPackageName(eNum.getEPackage()); } /** * Derives the package prefix of the given EMF <code>EAttribute</code>. * * @param eAttr * input <code>EAttribute</code> * @param rootPkgName * name of the graph schema's root package * @return package prefix of <code>eAttr</code> */ private static String getPackagePrefix(EAttribute eAttr) { return getPackagePrefix(eAttr.getEContainingClass()); } /** * Returns the qualified name of a package by iterating over all super * packages * * @param pkg * EMF package with a simple package name * @return qualified package name of <code>pkg</code> */ private static String getQualifiedName(EPackage pkg) { StringBuilder pkgName = new StringBuilder(); EPackage current = pkg; while (current != null) { pkgName.insert(0, current.getName()); if (current.getESuperPackage() != null) { pkgName.insert(0, '.'); } current = current.getESuperPackage(); } return pkgName.toString(); } /** * Derives a rolename from the given EMF <code>EReference</code>. If it's * null, use the given string (class name of the to vertex class and the * rolename of from) instead. * * @return rolenname derived from the parameters */ private static String getRoleName(EReference eRef, EClass to, EReference fromRef) { if (eRef != null) { return eRef.getName(); } else { return Character.toLowerCase(to.getName().charAt(0)) + to.getName().substring(1) + "From" + Character.toUpperCase(fromRef.getName().charAt(0)) + fromRef.getName().substring(1); } } /** * Gets the aggregation kind of VC2's incidence class by investigating the * both reference that describe one direction of an edge class.<br> * scenario: * * <pre> * VC1 --part1--> EC --part2--> VC2 * </pre> * * @param part1 * reference to edge class ("egdeClsIn") * @param part2 * reference from edge class ("egdeClsOut") * @return {@link AggregationKind#COMPOSITE}, if * <code>part1.isContainment() && part2.isContainment()</code><br> * {@link AggregationKind#NONE}, otherwise */ private static AggregationKind getAggregationKind(EReference part1, EReference part2) { if ((part1 != null) && part1.isContainment() && part2.isContainment()) { return AggregationKind.COMPOSITE; } else { return AggregationKind.NONE; } } /** * Derives the minimal multiplicity from the given <code>EReference</code>. * * @param eRef * input <code>EReference</code> * @return 0 if eRef is null or its lower bound is unspecified;<br> * Ref's lower bound otherwise */ private static int getMinMultiplicity(EReference eRef) { if ((eRef == null) || (eRef.getLowerBound() == EStructuralFeature.UNSPECIFIED_MULTIPLICITY)) { return 0; } else { return eRef.getLowerBound(); } } /** * Derives the minimal multiplicity from the given <code>EReference</code>. * * @param eRef * input <code>EReference</code> * @param aggr * AggregationKind of the opposite sid * @return 1 if aggr is composite and eRef specifies no other upper bound * value;<br> * <code>Integer.MAX_VALUE</code> if aggr is not composite and eRef * specifies no other upper bound value; eRef's upper bound * otherwise */ private static int getMaxMultiplicity(EReference eRef, AggregationKind aggr) { if ((aggr == AggregationKind.COMPOSITE) && ((eRef == null) || (eRef.getUpperBound() == EStructuralFeature.UNBOUNDED_MULTIPLICITY))) { return 1; } else if ((eRef == null) || (eRef.getUpperBound() == EStructuralFeature.UNBOUNDED_MULTIPLICITY)) { return Integer.MAX_VALUE; } else { return eRef.getUpperBound(); } } /** * Creates a name for an edge class from the given aggregation kind on the * from side, the name of the role on the to side and the name of the from * vertex class. * * @param aggrFrom * aggregation kind on the from side * @return */ private static String createEdgeClassName(AggregationKind aggrFrom, String toRoleName, String fromClsName) { toRoleName = Character.toUpperCase(toRoleName.charAt(0)) + toRoleName.substring(1); if ((aggrFrom != AggregationKind.NONE)) { return fromClsName + "Contains" + toRoleName; } else { return fromClsName + "LinksTo" + toRoleName; } } /** * Copies all annotations. * * @param from * source of the annotations * @param to * element that is annotated */ private static void copyAnnotations(EModelElement from, EModelElement to) { if ((from != null) && (to != null)) { to.getEAnnotations().addAll(from.getEAnnotations()); } } /** * Copies all properties from one <code>EPackage</code> to another one. * * @param from * source of the properties * @param to * <code>EPackage</code> that is provided with the properties */ private static void copyProperties(EPackage from, EPackage to) { if (from == null) { return; } else { to.setNsPrefix(from.getNsPrefix()); to.setNsURI(from.getNsURI()); } } /** * Copies all properties from one <code>EClass</code> to another one. * * @param from * source of the properties * @param to * target that is provided with the properties */ private static void copyProperties(EClass from, GrEMFVertexClassImpl to) { if (from == null) { return; } else { if (from.isAbstract()) { to.setAbstract(true); } if (from.isInterface()) { to.setInterface(true); } to.setClassifierID(from.getClassifierID()); } } /** * Copies all properties from one <code>EClass</code> to another one. * * @param from * source of the properties * @param to * target that is provided with the properties */ private static void copyProperties(EClass from, GrEMFEdgeClassImpl to) { if (from == null) { return; } else { to.setClassifierID(from.getClassifierID()); } } /** * Copies all properties from one <code>EReference</code> to another one. * * @param from * source of the properties * @param to * target that is provided with the properties */ private static void copyProperties(EReference from, GrEMFIncidenceClassImpl to) { if (from == null) { return; } else { to.setFeatureID(from.getFeatureID()); } } /** * Copies all properties from one <code>EAttribute</code> to another one. * * @param from * source of the properties * @param to * target that is provided with the properties */ private static void copyProperties(EAttribute from, GrEMFAttributeImpl to) { if (from == null) { return; } else { if (from.isID()) { to.setID(true); } to.setFeatureID(from.getFeatureID()); } } /** * Copies all properties from one <code>EEnum</code> to another one. * * @param from * source of the properties * @param to * target that is provided with the properties */ private static void copyProperties(EEnum from, GrEMFEnumDomainImpl to) { if (from == null) { return; } else { to.setClassifierID(from.getClassifierID()); } } }